K8s Pod 安全认知:从openshift SCC 到 PSP 弃用以及现在的 PSA

对每个人而言,真正的职责只有一个:找到自我。然后在心中坚守其一生,全心全意,永不停息。所有其它的路都是不完整的,是人的逃避方式,是对大众理想的懦弱回归,是随波逐流,是对内心的恐惧 ——赫尔曼·黑塞《德米安》

写在前面


  • 简单整理,博文内容涉及:
    • PSP 的由来
    • PSA 的发展
    • PSA 使用认知
  • 不涉及使用,用于了解 Pod 安全 API 资源
  • 理解不足小伙伴帮忙指正

对每个人而言,真正的职责只有一个:找到自我。然后在心中坚守其一生,全心全意,永不停息。所有其它的路都是不完整的,是人的逃避方式,是对大众理想的懦弱回归,是随波逐流,是对内心的恐惧 ——赫尔曼·黑塞《德米安》


对于和 Docker 类似,通过 Linux namespace 和 Cgroup 实现的一类 容器技术,本质上,容器就是 宿主节点的一个进程,处于安全考虑,最小权限原则,在生产中,很少使用 Root 来运行一些业务进程,即很少通过 根用户 来运行相关容器。避免 特权升级,容器逃逸,以及利用不必要权限来增加意外操作或者恶意行为。

PSP 的由来

在 K8s 中,通过准入控制器来限制 Pod 的创建,当一个 apply 的 Pod 满足全部准入控制器时,才会被创建。对于 Pod 安全,K8s 也提供了自己的准入控制器。讲 K8s 的 安全策略,不得不提 openshift 的 SCC

Kubernetes 1.0 于 2015 年 7 月 10 日发布,除了 Alpha 阶段的 SecurityContextDeny 准入插件 (当时称为 scdeny)之外, 没有任何机制来限制安全上下文和工作负载的敏感选项

PSP 即为 K8s 最早的安全策略,即 PodSecurityPolicy ,PodSecurityPolicy 源自 OpenShift SecurityContextConstraints (SCC), SCC 出现在 Red Hat OpenShift 容器平台的第一个版本中,甚至在 Kubernetes 1.0 之前。PSP 是 SCC 的精简版。

SCC

熟悉 openshift 的小伙伴,或者参加过红帽 DO280 考试的小伙伴的,对 SCC 一定不陌生,SCC 即 SecurityContextConstraints(容器安全上下文) ,是 openshift 的一直沿用的 Pod 安全策略。

利用 SCC ,管理员能对Pod做如下的一些约束:

  • 运行特权容器 (privileged container)
  • 为容器增加能力 (capahiliticg)
  • 用主机上的目录作为卷
  • 容器的 SELinux 下文
  • 运行用户 ID
  • 主机命名空间和网络
  • 为 Pod 的卷分配 FSGroup
  • 配置允许的补充组
  • 要求使用只读文件系统
  • 控制允许使用的卷类型
  • 控制允许使用的安全计算模式配置文件(seccop prorile)

查看当前命名空间下的所以的 SSC , OpenShift 有七个 SCC 级别

1
2
3
4
5
6
7
8
9
10
[root@master student]# oc get scc
NAME PRIV CAPS SELINUX RUNASUSER FSGROUP SUPGROUP PRIORITY READONLYROOTFS VOLUMES
anyuid false [] MustRunAs RunAsAny RunAsAny RunAsAny 10 false [configMap downwardAPI emptyDir persistentVolumeClaim projected secret]
hostaccess false [] MustRunAs MustRunAsRange MustRunAs RunAsAny <none> false [configMap downwardAPI emptyDir hostPath persistentVolumeClaim projected secret]
hostmount-anyuid false [] MustRunAs RunAsAny RunAsAny RunAsAny <none> false [configMap downwardAPI emptyDir hostPath nfs persistentVolumeClaim projected secret]
hostnetwork false [] MustRunAs MustRunAsRange MustRunAs MustRunAs <none> false [configMap downwardAPI emptyDir persistentVolumeClaim projected secret]
nonroot false [] MustRunAs MustRunAsNonRoot RunAsAny RunAsAny <none> false [configMap downwardAPI emptyDir persistentVolumeClaim projected secret]
privileged true [*] RunAsAny RunAsAny RunAsAny RunAsAny <none> false [*]
restricted false [] MustRunAs MustRunAsRange MustRunAs RunAsAny <none> false [configMap downwardAPI emptyDir persistentVolumeClaim projected secret]
[root@master student]#

默认情况下,任何容器的执行都只授予 restricted(受限制) 的 SCC,这个 SCC 是最严格的,适用于以非 root 权限运行的 pod。它限制了 pod 对主机文件系统和网络的访问。

对应 的7中 SCC 限制说明:

  1. restricted:这个 SCC 是最严格的,适用于以 非root 权限运行的 pod。它限制了 pod 对主机文件系统和网络的访问。
  2. privileged:这个 SCC 允许 pod 以完整的 root 权限运行,并访问所有主机资源。它适用于需要访问敏感主机资源的 pod。
  3. anyuid:这个 SCC 允许 pod 以任何用户 ID 和组 ID 运行。它适用于需要访问默认用户 ID 不可访问的主机资源的 pod。
  4. hostaccess:这个 SCC 允许 pod 挂载主机文件系统并访问主机设备。它适用于需要访问默认用户 ID 不可访问的主机资源的 pod。
  5. hostmount-anyuid:这个 SCC 允许 pod 挂载主机文件系统并以任何用户 ID 和组 ID 运行。它适用于需要访问默认用户 ID 不可访问的主机资源的 pod。
  6. nonroot:这个 SCC 适用于不需要 root 权限的 pod。它限制了 pod 对主机文件系统和网络的访问。
  7. hostnetwork: 这个 SCC 允许 pod 使用主机网络命名空间。这意味着 pod 可以访问主机上的网络接口和端口,而不是被限制在容器网络命名空间中。这个 SCC 可能会增加 pod 对主机网络的访问权限,因此需要谨慎使用。

PSP

PodSecurityPolicy 与其他专门的准入控制插件一样,作为内置的策略 API,对有关 Pod 安全设置的特定字段提供细粒度的权限。 它承认集群管理员和集群用户通常不是同一个人,并且以 Pod 形式或任何将创建 Pod 的资源的形式创建工作负载的权限不应该等同于“集群上的 root 账户”。 它还可以通过变更配置来应用更安全的默认值,并将底层 Linux 安全决策与部署过程分离来促进最佳实践。

使用 PodSecurityPolicy 的根源是早期关于安全策略的一个拉取请求, 它以 SCC(安全上下文约束)为基础,增加了新的 PSP 对象的设计方案。这是一个长达 9 个月的漫长讨论, 基于 OpenShift 的 SCC 反复讨论, 多次变动,并重命名为 PodSecurityPolicy,最终在 2016 年 2 月进入上游 Kubernetes。 现在 PSP 对象已经创建,下一步是添加一个可以执行这些政策的准入控制器。 第一步是添加不考虑用户或组 的准入控制。 2016 年 5 月,一个特定的使 PodSecurityPolicy 达到可用状态的问题被添加进来, 以跟踪进展,并在名为 PSP 准入的拉取请求中合并了准入控制器的第一个版本。 然后大约两个月后,发布了 Kubernetes 1.3

在这里插入图片描述

之后,PSP 准入控制器通过添加最初被搁置的内容进行了增强。 在 2016 年 11 月上旬合并鉴权机制, 允许管理员在集群中使用多个策略,为不同类型的用户授予不同级别的访问权限。 后来,2017 年 10 月合并的一个拉取请求 修复了 PodSecurityPolicies 在变更和字母顺序之间冲突的设计问题, 并继续构建我们所知道的 PSP 准入。之后,进行了许多改进和修复,以构建最近 Kubernetes 版本的 PodSecurityPolicy 功能。

对于 PSP 的使用,需要做 api-server 组件中启用配置 ,在 kube-apiserver 组件的启动配置中添加 --enable-admission-plugins=PodSecurityPolicy 参数

创建 PodSecurityPolicy,创建了一个名为 “restricted-psp” 的 PodSecurityPolicy,其中容器不能以特权模式运行,必须以非 root 用户身份运行,并且在文件系统权限和存储卷方面宽松一些。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: restricted-psp
spec:
privileged: false
seLinux:
rule: RunAsAny
runAsUser:
rule: MustRunAsNonRoot
fsGroup:
rule: RunAsAny
volumes:
- '*'

上述示例创建了一个名为 “restricted-psp” 的 PodSecurityPolicy,其中容器不能以特权模式运行,必须以非 root 用户身份运行,并且在文件系统权限和存储卷方面宽松一些。

配置 PodSecurityPolicy: PSP 直接和角色绑定,然后通过角色绑定用户或者 SA 来实现 pod 对 PSP 的应用。

  • 创建一个 ClusterRole,并将 PodSecurityPolicy 绑定到该 ClusterRole。
  • 创建一个 ClusterRoleBinding,将该 ClusterRole 绑定到您希望使用该 PodSecurityPolicy 的用户、服务账号或命名空间。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: psp-demo-role
rules:
- apiGroups:
- policy
resources:
- podsecuritypolicies
verbs:
- use
resourceNames:
- restricted-psp

---

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: psp-demo-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: psp-demo-role
subjects:
- kind: User
name: <your-username>

通过 服务账号 sa 的方式绑定,在之后的 pod 创建中,使用对应的服务账号创建 Pod 即可应用 PSP 对应的策略

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: my-rolebinding
namespace: my-namespace
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: psp:restricted-psp
subjects:
- kind: ServiceAccount
name: my-serviceaccount
namespace: my-namespace

PSA 的发展

尽管 PodSecurityPolicy 试图解决的是一个关键问题,但它却包含一些重大缺陷:

  • 有缺陷的鉴权模式 - 如果用户针对 PSP 具有执行 use 动作的权限,而此 PSP 准许该 Pod 或者该 Pod 的服务帐户对 PSP 执行 use 操作,则用户可以创建一个 Pod。
  • 难以推广 - PSP 失败关闭。也就是说,在没有策略的情况下,所有 Pod 都会被拒绝。 这主要意味着默认情况下无法启用它,并且用户必须在启用该功能之前为所有工作负载添加 PSP
  • 不一致的无边界 API - API 的发展有很多不一致的地方,特别是由于许多小众场景的请求: 如标签、调度、细粒度的卷控制等。它的可组合性很差,优先级模型较弱,会导致意外的变更优先级。 这使得 PSP 与其他第三方准入控制器的结合真的很困难。
  • 需要安全知识 - 有效使用 PSP 仍然需要了解 Linux 的安全原语。 例如:MustRunAsNonRoot + AllowPrivilegeEscalation

PodSecurityPolicy 的经验得出的结论是,大多数用户关心两个或三个策略,这导致了 Pod 安全标准(PSS)的创建,它定义了三个策略

  • Privileged(特权的): 不受限制的策略,提供最大可能范围的权限许可。此策略允许已知的特权提升。
  • Baseline(基线的): 限制性最弱的策略,禁止已知的策略提升。允许使用默认的(规定最少)Pod 配置。
  • Restricted(受限的): 限制性非常强的策略,遵循当前的保护 Pod 的最佳实践

作为 PSP 的替代品,新的 Pod 安全准入(Pod Security Admission ,PSA) 是 Kubernetes v1.25 的稳定的准入插件,用于在命名空间级别强制执行这些标准。 无需深入的安全知识,就可以更轻松地实施基本的 Pod 安全性。

Kubernetes 1.21版本开始,PodSecurityPolicy(PSP)已被标记为已弃用,,从 Kubernetes v1.25 开始,PodSecurityPolicy (PSP) 准入控制器已被移除

作为 PSP 的替代品,新的 Pod 安全性准入(Pod Security Admission)在 Kubernetes v1.22 作为 Alpha 特性发布, 在 Kubernetes v1.23 中作为 Beta 特性默认可用。从 1.25 版本起, 此特性进阶至正式发布(Generally Available)。用于在命名空间级别强制执行这些标准。 无需深入的安全知识,就可以更轻松地实施基本的 Pod 安全性。

Kubernetes 1.21版本开始,PodSecurityPolicy(PSP)已被标记为已弃用,,从 Kubernetes v1.25 开始PodSecurityPolicy (PSP) 准入控制器已被移除

Pod 安全性标准 PSS

Pod 安全性标准定义了三种不同的策略(Policy),以广泛覆盖安全应用场景。 这些策略是叠加式的(Cumulative),安全级别从高度宽松至高度受限。 本指南概述了每个策略的要求。

三个分类对应的权限控制,细粒度对应 securityContext 安全上下文对象中的不同的值,可以立即为把 securityContext 中属性按照安全级别分为了三个大类:这里的分类类似 openshift 中到的 7类 SCC。

Privileged

Privileged 策略是有目的地开放且完全无限制的策略。 此类策略通常针对由特权较高、受信任的用户所管理的系统级或基础设施级负载

Privileged 策略定义中限制较少。默认允许的(Allow-by-default)实施机制(例如 gatekeeper) 可以缺省设置为 Privileged。 与此不同,对于默认拒绝(Deny-by-default)的实施机制(如 Pod 安全策略)而言, Privileged 策略应该禁止所有限制。

Baseline

Baseline 策略的目标是便于常见的容器化应用采用,同时禁止已知的特权提升。 此策略针对的是应用运维人员和非关键性应用的开发人员。

Restricted

Restricted 策略旨在实施当前保护 Pod 的最佳实践,尽管这样作可能会牺牲一些兼容性。 该类策略主要针对运维人员和安全性很重要的应用的开发人员,以及不太被信任的用户。

安全性准入控制配置

回到安全准入控制 PSA,在集群中,配置安全准入控制,有两种方式:

  • 为命名空间设置 Pod 安全性准入控制标签
  • 集群范围的安全准入控制器配置

命名空间设置Pod 安全性准入控制标签

为命名空间设置 Pod 安全性准入控制标签,需要在命名空间API 对象上添加对应的标签,通过标签来控制Pod安全准入控制。

安全准入控制标签,通过 MODE LEVEL 进行准入限制, LEVEL 用于设置安全级别 PSS,MODE 用于设置命中策略后的处理逻辑

MODE 也同样分为三类:

  • enforce:策略违例会导致 Pod 被拒绝
  • audit: 策略违例会触发审计日志中记录新事件时添加审计注解;但是 Pod 仍是被接受的。
  • warn: 策略违例会触发用户可见的警告信息,但是 Pod 仍是被接受的。

命名空间可以配置任何一种或者所有模式,或者甚至为不同的模式设置不同的级别。 为了尽早地捕获违例状况,auditwarn 模式都应用到负载资源enforce 模式并不应用到工作负载资源,仅应用到所生成的 Pod 对象上。

对于每种模式,决定所使用策略的标签有两个:

1
2
3
4
5
6
7
8
9
10
11
12
# 模式的级别标签用来标示对应模式所应用的策略级别
#
# MODE 必须是 `enforce`、`audit` 或 `warn` 之一
# LEVEL 必须是 `privileged`、baseline` 或 `restricted` 之一
pod-security.kubernetes.io/<MODE>: <LEVEL>

# 可选:针对每个模式版本的版本标签可以将策略锁定到
# 给定 Kubernetes 小版本号所附带的版本(例如 v1.28)
#
# MODE 必须是 `enforce`、`audit` 或 `warn` 之一
# VERSION 必须是一个合法的 Kubernetes 小版本号或者 `latest`
pod-security.kubernetes.io/<MODE>-version: <VERSION>

配置 baseline Pod 容器标准准入

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
kind: Namespace
metadata:
name: my-baseline-namespace
labels:
pod-security.kubernetes.io/enforce: baseline
pod-security.kubernetes.io/enforce-version: v1.28

# 我们将这些标签设置为我们所 _期望_ 的 `enforce` 级别
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/audit-version: v1.28
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/warn-version: v1.28

使用 kubectl label 为现有名字空间添加标签

在刚开始为名字空间评估安全性策略变更时,使用 –dry-run 标志是很有用的。 Pod 安全性标准会在 dry run(试运行) 模式下运行,在这种模式下会生成新策略如何处理现有 Pod 的信息, 但不会真正更新策略。

1
2
kubectl label --dry-run=server --overwrite ns --all \
pod-security.kubernetes.io/enforce=baseline

应用到所有名字空间

如果你是刚刚开始使用 Pod 安全性标准,一种比较合适的初始步骤是针对所有名字空间为类似 baseline 这种比较严格的安全级别配置审计注解。

1
2
3
kubectl label --overwrite ns --all \
pod-security.kubernetes.io/audit=baseline \
pod-security.kubernetes.io/warn=baseline

查看没有显式设置 enforce 级别的名字空间

1
kubectl get namespaces --selector='!pod-security.kubernetes.io/enforce'

应用到单个名字空间

1
2
3
kubectl label --overwrite ns my-existing-namespace \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/enforce-version=v1.28

集群范围的安全准入控制器配置

可以为 Pod 安全性的实施设置豁免(Exemptions) 规则, 从而允许创建一些本来会被与给定名字空间相关的策略所禁止的 Pod。 豁免规则可以在内置准入控制器配置静态配置

Kubernetes 提供的内置的准入控制器 用来强制实施 Pod 安全性标准。 除了豁免选项,也可以配置此准入控制器来设置集群范围的默认值

豁免规则必须显式枚举。满足豁免标准的请求会被准入控制器忽略 (所有 enforce、audit 和 warn 行为都会被略过)。 豁免的维度包括:

  • Username: 来自用户名已被豁免的、已认证的(或伪装的)的用户的请求会被忽略。
  • RuntimeClassName: 指定了已豁免的运行时类名称的 Pod + 和负载资源会被忽略。
  • Namespace: 位于被豁免的名字空间中的 Pod 和负载资源会被忽略。

大多数 Pod 是作为对工作负载资源的响应, 由控制器所创建的,这意味着

  • 为某最终用户提供豁免时,只会当该用户直接创建 Pod 时对其实施安全策略的豁免。用户创建工作负载资源时不会被豁免。
  • 控制器服务账号(例如system:serviceaccount:kube-system:replicaset-controller) 通常不应该被豁免,因为豁免这类服务账号隐含着对所有能够创建对应工作负载资源的用户豁免。

pod-security.admission.config.k8s.io/v1 配置需要 v1.25+。 对于 v1.23 和 v1.24,使用 v1beta1。 对于 v1.22,使用 v1alpha1。

需要说明的是,下面的 yaml 文件需要通过 ——admission-control-config-file 指定到 kube-apiserver,不能直接应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
apiVersion: apiserver.config.k8s.io/v1 # 查阅兼容性说明
kind: AdmissionConfiguration
plugins:
- name: PodSecurity
configuration:
apiVersion: pod-security.admission.config.k8s.io/v1
kind: PodSecurityConfiguration
# 当未设置 mode 标签时会应用的默认设置
#
# level 标签必须是以下取值之一:
# - "privileged" (默认)
# - "baseline"
# - "restricted"
#
# version 标签必须是如下取值之一:
# - "latest" (默认)
# - 诸如 "v1.28" 这类版本号
defaults:
enforce: "privileged"
enforce-version: "latest"
audit: "privileged"
audit-version: "latest"
warn: "privileged"
warn-version: "latest"
exemptions:
# 要豁免的已认证用户名列表
usernames: []
# 要豁免的运行时类名称列表
runtimeClasses: []
# 要豁免的名字空间列表
namespaces: []

博文部分内容参考

© 文中涉及参考链接内容版权归原作者所有,如有侵权请告知,这是一个开源项目,如果你认可它,不要吝啬星星哦 :)


https://kubernetes.io/zh-cn/blog/2022/08/23/podsecuritypolicy-the-historical-context/

https://kubernetes.io/zh-cn/docs/concepts/security/pod-security-admission/

https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/enforce-standards-namespace-labels/


© 2018-2023 liruilonger@gmail.com, All rights reserved. 保持署名-非商用-相同方式共享(CC BY-NC-SA 4.0)

发布于

2023-08-27

更新于

2023-08-28

许可协议

评论
Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×